import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.awt.image.*;
import javax.swing.border.*;
/**
* Displays a picture and lets you explore the picture by displaying the row, column, red,
* green, and blue values of the pixel at the cursor when you click a mouse button or
* press and hold a mouse button while moving the cursor. It also lets you zoom in or
* out. You can also type in a row and column value to see the color at that location.
*
* Originally created for the Jython Environment for Students (JES).
* Modified to work with DrJava by Barbara Ericson
* Also modified to show row and columns by Barbara Ericson
*
* @author Keith McDermottt, gte047w@cc.gatech.edu
* @author Barb Ericson ericson@cc.gatech.edu
*/
public class PictureExplorer implements MouseMotionListener, ActionListener, MouseListener
{
// current indicies
/** row index */
private int rowIndex = 0;
/** column index */
private int colIndex = 0;
// main GUI
/** window to hold GUI */
private JFrame pictureFrame;
/** window that allows the user to scroll to see a large picture */
private JScrollPane scrollPane;
// GUI components
/** column label */
private JLabel colLabel;
/** column previous button */
private JButton colPrevButton;
/** row previous button */
private JButton rowPrevButton;
/** column next button */
private JButton colNextButton;
/** row next button */
private JButton rowNextButton;
/** row label */
private JLabel rowLabel;
/** text field to show column index */
private JTextField colValue;
/** text field to show row index */
private JTextField rowValue;
/** red value label */
private JLabel rValue;
/** green value label */
private JLabel gValue;
/** blue value label */
private JLabel bValue;
/** color swatch label */
private JLabel colorLabel;
/** panel to show the color swatch */
private JPanel colorPanel;
// menu components
/** menu bar */
private JMenuBar menuBar;
/** zoom menu */
private JMenu zoomMenu;
/** 25% zoom level */
private JMenuItem twentyFive;
/** 50% zoom level */
private JMenuItem fifty;
/** 75% zoom level */
private JMenuItem seventyFive;
/** 100% zoom level */
private JMenuItem hundred;
/** 150% zoom level */
private JMenuItem hundredFifty;
/** 200% zoom level */
private JMenuItem twoHundred;
/** 500% zoom level */
private JMenuItem fiveHundred;
/** The picture being explored */
private DigitalPicture picture;
/** The image icon used to display the picture */
private ImageIcon scrollImageIcon;
/** The image display */
private ImageDisplay imageDisplay;
/** the zoom factor (amount to zoom) */
private double zoomFactor;
/** the number system to use, 0 means starting at 0, 1 means starting at 1 */
private int numberBase=0;
/**
* Public constructor
* @param picture the picture to explore
*/
public PictureExplorer(DigitalPicture picture)
{
// set the fields
this.picture=picture;
zoomFactor=1;
// create the window and set things up
createWindow();
}
/**
* Changes the number system to start at one
*/
public void changeToBaseOne()
{
numberBase=1;
}
/**
* Set the title of the frame
*@param title the title to use in the JFrame
*/
public void setTitle(String title)
{
pictureFrame.setTitle(title);
}
/**
* Method to create and initialize the picture frame
*/
private void createAndInitPictureFrame()
{
pictureFrame = new JFrame(); // create the JFrame
pictureFrame.setResizable(true); // allow the user to resize it
pictureFrame.getContentPane().setLayout(new BorderLayout()); // use border layout
pictureFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); // when close stop
pictureFrame.setTitle(picture.getTitle());
PictureExplorerFocusTraversalPolicy newPolicy = new PictureExplorerFocusTraversalPolicy();
pictureFrame.setFocusTraversalPolicy(newPolicy);
}
/**
* Method to create the menu bar, menus, and menu items
*/
private void setUpMenuBar()
{
//create menu
menuBar = new JMenuBar();
zoomMenu = new JMenu("Zoom");
twentyFive = new JMenuItem("25%");
fifty = new JMenuItem("50%");
seventyFive = new JMenuItem("75%");
hundred = new JMenuItem("100%");
hundred.setEnabled(false);
hundredFifty = new JMenuItem("150%");
twoHundred = new JMenuItem("200%");
fiveHundred = new JMenuItem("500%");
// add the action listeners
twentyFive.addActionListener(this);
fifty.addActionListener(this);
seventyFive.addActionListener(this);
hundred.addActionListener(this);
hundredFifty.addActionListener(this);
twoHundred.addActionListener(this);
fiveHundred.addActionListener(this);
// add the menu items to the menus
zoomMenu.add(twentyFive);
zoomMenu.add(fifty);
zoomMenu.add(seventyFive);
zoomMenu.add(hundred);
zoomMenu.add(hundredFifty);
zoomMenu.add(twoHundred);
zoomMenu.add(fiveHundred);
menuBar.add(zoomMenu);
// set the menu bar to this menu
pictureFrame.setJMenuBar(menuBar);
}
/**
* Create and initialize the scrolling image
*/
private void createAndInitScrollingImage()
{
scrollPane = new JScrollPane();
BufferedImage bimg = picture.getBufferedImage();
imageDisplay = new ImageDisplay(bimg);
imageDisplay.addMouseMotionListener(this);
imageDisplay.addMouseListener(this);
imageDisplay.setToolTipText("Click a mouse button on a pixel to see the pixel information");
scrollPane.setViewportView(imageDisplay);
pictureFrame.getContentPane().add(scrollPane, BorderLayout.CENTER);
}
/**
* Creates the JFrame and sets everything up
*/
private void createWindow()
{
// create the picture frame and initialize it
createAndInitPictureFrame();
// set up the menu bar
setUpMenuBar();
//create the information panel
createInfoPanel();
//creates the scrollpane for the picture
createAndInitScrollingImage();
// show the picture in the frame at the size it needs to be
pictureFrame.pack();
pictureFrame.setVisible(true);
}
/**
* Method to set up the next and previous buttons for the
* pixel location information
*/
private void setUpNextAndPreviousButtons()
{
// create the image icons for the buttons
Icon prevIcon = new ImageIcon(DigitalPicture.class.getResource("leftArrow.gif"),
"previous index");
Icon nextIcon = new ImageIcon(DigitalPicture.class.getResource("rightArrow.gif"),
"next index");
// create the arrow buttons
colPrevButton = new JButton(prevIcon);
colNextButton = new JButton(nextIcon);
rowPrevButton = new JButton(prevIcon);
rowNextButton = new JButton(nextIcon);
// set the tool tip text
colNextButton.setToolTipText("Click to go to the next column value");
colPrevButton.setToolTipText("Click to go to the previous column value");
rowNextButton.setToolTipText("Click to go to the next row value");
rowPrevButton.setToolTipText("Click to go to the previous row value");
// set the sizes of the buttons
int prevWidth = prevIcon.getIconWidth() + 2;
int nextWidth = nextIcon.getIconWidth() + 2;
int prevHeight = prevIcon.getIconHeight() + 2;
int nextHeight = nextIcon.getIconHeight() + 2;
Dimension prevDimension = new Dimension(prevWidth,prevHeight);
Dimension nextDimension = new Dimension(nextWidth, nextHeight);
colPrevButton.setPreferredSize(prevDimension);
rowPrevButton.setPreferredSize(prevDimension);
colNextButton.setPreferredSize(nextDimension);
rowNextButton.setPreferredSize(nextDimension);
// handle previous column button press
colPrevButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
colIndex--;
if (colIndex < 0)
colIndex = 0;
displayPixelInformation(colIndex,rowIndex);
}
});
// handle previous row button press
rowPrevButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
rowIndex--;
if (rowIndex < 0)
rowIndex = 0;
displayPixelInformation(colIndex,rowIndex);
}
});
// handle next column button press
colNextButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
colIndex++;
if (colIndex >= picture.getWidth())
colIndex = picture.getWidth() - 1;
displayPixelInformation(colIndex,rowIndex);
}
});
// handle next row button press
rowNextButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
rowIndex++;
if (rowIndex >= picture.getHeight())
rowIndex = picture.getHeight() - 1;
displayPixelInformation(colIndex,rowIndex);
}
});
}
/**
* Create the pixel location panel
* @param labelFont the font for the labels
* @return the location panel
*/
public JPanel createLocationPanel(Font labelFont) {
// create a location panel
JPanel locationPanel = new JPanel();
locationPanel.setLayout(new FlowLayout());
Box hBox = Box.createHorizontalBox();
// create the labels
rowLabel = new JLabel("Row:");
colLabel = new JLabel("Column:");
// create the text fields
colValue = new JTextField(Integer.toString(colIndex + numberBase),6);
colValue.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
displayPixelInformation(colValue.getText(),rowValue.getText());
}
});
rowValue = new JTextField(Integer.toString(rowIndex + numberBase),6);
rowValue.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
displayPixelInformation(colValue.getText(),rowValue.getText());
}
});
// set up the next and previous buttons
setUpNextAndPreviousButtons();
// set up the font for the labels
colLabel.setFont(labelFont);
rowLabel.setFont(labelFont);
colValue.setFont(labelFont);
rowValue.setFont(labelFont);
// add the items to the vertical box and the box to the panel
hBox.add(Box.createHorizontalGlue());
hBox.add(rowLabel);
hBox.add(rowPrevButton);
hBox.add(rowValue);
hBox.add(rowNextButton);
hBox.add(Box.createHorizontalStrut(10));
hBox.add(colLabel);
hBox.add(colPrevButton);
hBox.add(colValue);
hBox.add(colNextButton);
locationPanel.add(hBox);
hBox.add(Box.createHorizontalGlue());
return locationPanel;
}
/**
* Create the color information panel
* @param labelFont the font to use for labels
* @return the color information panel
*/
private JPanel createColorInfoPanel(Font labelFont)
{
// create a color info panel
JPanel colorInfoPanel = new JPanel();
colorInfoPanel.setLayout(new FlowLayout());
// get the pixel at the x and y
Pixel pixel = new Pixel(picture,colIndex,rowIndex);
// create the labels
rValue = new JLabel("R: " + pixel.getRed());
gValue = new JLabel("G: " + pixel.getGreen());
bValue = new JLabel("B: " + pixel.getBlue());
// create the sample color panel and label
colorLabel = new JLabel("Color at location: ");
colorPanel = new JPanel();
colorPanel.setBorder(new LineBorder(Color.black,1));
// set the color sample to the pixel color
colorPanel.setBackground(pixel.getColor());
// set the font
rValue.setFont(labelFont);
gValue.setFont(labelFont);
bValue.setFont(labelFont);
colorLabel.setFont(labelFont);
colorPanel.setPreferredSize(new Dimension(25,25));
// add items to the color information panel
colorInfoPanel.add(rValue);
colorInfoPanel.add(gValue);
colorInfoPanel.add(bValue);
colorInfoPanel.add(colorLabel);
colorInfoPanel.add(colorPanel);
return colorInfoPanel;
}
/**
* Creates the North JPanel with all the pixel location
* and color information
*/
private void createInfoPanel()
{
// create the info panel and set the layout
JPanel infoPanel = new JPanel();
infoPanel.setLayout(new BorderLayout());
// create the font
Font largerFont = new Font(infoPanel.getFont().getName(),
infoPanel.getFont().getStyle(),14);
// create the pixel location panel
JPanel locationPanel = createLocationPanel(largerFont);
// create the color information panel
JPanel colorInfoPanel = createColorInfoPanel(largerFont);
// add the panels to the info panel
infoPanel.add(BorderLayout.NORTH,locationPanel);
infoPanel.add(BorderLayout.SOUTH,colorInfoPanel);
// add the info panel
pictureFrame.getContentPane().add(BorderLayout.NORTH,infoPanel);
}
/**
* Method to check that the current position is in the viewing area and if
* not scroll to center the current position if possible
*/
public void checkScroll()
{
// get the x and y position in pixels
int xPos = (int) (colIndex * zoomFactor);
int yPos = (int) (rowIndex * zoomFactor);
// only do this if the image is larger than normal
if (zoomFactor > 1) {
// get the rectangle that defines the current view
JViewport viewport = scrollPane.getViewport();
Rectangle rect = viewport.getViewRect();
int rectMinX = (int) rect.getX();
int rectWidth = (int) rect.getWidth();
int rectMaxX = rectMinX + rectWidth - 1;
int rectMinY = (int) rect.getY();
int rectHeight = (int) rect.getHeight();
int rectMaxY = rectMinY + rectHeight - 1;
// get the maximum possible x and y index
int macolIndexX = (int) (picture.getWidth() * zoomFactor) - rectWidth - 1;
int macolIndexY = (int) (picture.getHeight() * zoomFactor) - rectHeight - 1;
// calculate how to position the current position in the middle of the viewing
// area
int viewX = xPos - (int) (rectWidth / 2);
int viewY = yPos - (int) (rectHeight / 2);
// reposition the viewX and viewY if outside allowed values
if (viewX < 0)
viewX = 0;
else if (viewX > macolIndexX)
viewX = macolIndexX;
if (viewY < 0)
viewY = 0;
else if (viewY > macolIndexY)
viewY = macolIndexY;
// move the viewport upper left point
viewport.scrollRectToVisible(new Rectangle(viewX,viewY,rectWidth,rectHeight));
}
}
/**
* Zooms in the on picture by scaling the image.
* It is extremely memory intensive.
* @param factor the amount to zoom by
*/
public void zoom(double factor)
{
// save the current zoom factor
zoomFactor = factor;
// calculate the new width and height and get an image that size
int width = (int) (picture.getWidth()*zoomFactor);
int height = (int) (picture.getHeight()*zoomFactor);
BufferedImage bimg = picture.getBufferedImage();
// set the scroll image icon to the new image
imageDisplay.setImage(bimg.getScaledInstance(width, height, Image.SCALE_DEFAULT));
imageDisplay.setCurrentX((int) (colIndex * zoomFactor));
imageDisplay.setCurrentY((int) (rowIndex * zoomFactor));
imageDisplay.revalidate();
checkScroll(); // check if need to reposition scroll
}
/**
* Repaints the image on the scrollpane.
*/
public void repaint()
{
pictureFrame.repaint();
}
//****************************************//
// Event Listeners //
//****************************************//
/**
* Called when the mouse is dragged (button held down and moved)
* @param e the mouse event
*/
public void mouseDragged(MouseEvent e)
{
displayPixelInformation(e);
}
/**
* Method to check if the given x and y are in the picture
* @param column the horizontal value
* @param row the vertical value
* @return true if the row and column are in the picture
* and false otherwise
*/
private boolean isLocationInPicture(int column, int row)
{
boolean result = false; // the default is false
if (column >= 0 && column < picture.getWidth() &&
row >= 0 && row < picture.getHeight())
result = true;
return result;
}
/**
* Method to display the pixel information from the passed x and y but
* also converts x and y from strings
* @param xString the x value as a string from the user
* @param yString the y value as a string from the user
*/
public void displayPixelInformation(String xString, String yString)
{
int x = -1;
int y = -1;
try {
x = Integer.parseInt(xString);
x = x - numberBase;
y = Integer.parseInt(yString);
y = y - numberBase;
} catch (Exception ex) {
}
if (x >= 0 && y >= 0) {
displayPixelInformation(x,y);
}
}
/**
* Method to display pixel information for the passed x and y
* @param pictureX the x value in the picture
* @param pictureY the y value in the picture
*/
private void displayPixelInformation(int pictureX, int pictureY)
{
// check that this x and y are in range
if (isLocationInPicture(pictureX, pictureY))
{
// save the current x and y index
colIndex = pictureX;
rowIndex = pictureY;
// get the pixel at the x and y
Pixel pixel = new Pixel(picture,colIndex,rowIndex);
// set the values based on the pixel
colValue.setText(Integer.toString(colIndex + numberBase));
rowValue.setText(Integer.toString(rowIndex + numberBase));
rValue.setText("R: " + pixel.getRed());
gValue.setText("G: " + pixel.getGreen());
bValue.setText("B: " + pixel.getBlue());
colorPanel.setBackground(new Color(pixel.getRed(), pixel.getGreen(), pixel.getBlue()));
}
else
{
clearInformation();
}
// notify the image display of the current x and y
imageDisplay.setCurrentX((int) (colIndex * zoomFactor));
imageDisplay.setCurrentY((int) (rowIndex * zoomFactor));
}
/**
* Method to display pixel information based on a mouse event
* @param e a mouse event
*/
private void displayPixelInformation(MouseEvent e)
{
// get the cursor x and y
int cursorX = e.getX();
int cursorY = e.getY();
// get the x and y in the original (not scaled image)
int pictureX = (int) (cursorX / zoomFactor + numberBase);
int pictureY = (int) (cursorY / zoomFactor + numberBase);
// display the information for this x and y
displayPixelInformation(pictureX,pictureY);
}
/**
* Method to clear the labels and current color and reset the
* current index to -1
*/
private void clearInformation()
{
colValue.setText("N/A");
rowValue.setText("N/A");
rValue.setText("R: N/A");
gValue.setText("G: N/A");
bValue.setText("B: N/A");
colorPanel.setBackground(Color.black);
colIndex = -1;
rowIndex = -1;
}
/**
* Method called when the mouse is moved with no buttons down
* @param e the mouse event
*/
public void mouseMoved(MouseEvent e)
{}
/**
* Method called when the mouse is clicked
* @param e the mouse event
*/
public void mouseClicked(MouseEvent e)
{
displayPixelInformation(e);
}
/**
* Method called when the mouse button is pushed down
* @param e the mouse event
*/
public void mousePressed(MouseEvent e)
{
displayPixelInformation(e);
}
/**
* Method called when the mouse button is released
* @param e the mouse event
*/
public void mouseReleased(MouseEvent e)
{
}
/**
* Method called when the component is entered (mouse moves over it)
* @param e the mouse event
*/
public void mouseEntered(MouseEvent e)
{
}
/**
* Method called when the mouse moves over the component
* @param e the mouse event
*/
public void mouseExited(MouseEvent e)
{
}
/**
* Method to enable all menu commands
*/
private void enableZoomItems()
{
twentyFive.setEnabled(true);
fifty.setEnabled(true);
seventyFive.setEnabled(true);
hundred.setEnabled(true);
hundredFifty.setEnabled(true);
twoHundred.setEnabled(true);
fiveHundred.setEnabled(true);
}
/**
* Controls the zoom menu bar
*
* @param a the ActionEvent
*/
public void actionPerformed(ActionEvent a)
{
if(a.getActionCommand().equals("Update"))
{
this.repaint();
}
if(a.getActionCommand().equals("25%"))
{
this.zoom(.25);
enableZoomItems();
twentyFive.setEnabled(false);
}
if(a.getActionCommand().equals("50%"))
{
this.zoom(.50);
enableZoomItems();
fifty.setEnabled(false);
}
if(a.getActionCommand().equals("75%"))
{
this.zoom(.75);
enableZoomItems();
seventyFive.setEnabled(false);
}
if(a.getActionCommand().equals("100%"))
{
this.zoom(1.0);
enableZoomItems();
hundred.setEnabled(false);
}
if(a.getActionCommand().equals("150%"))
{
this.zoom(1.5);
enableZoomItems();
hundredFifty.setEnabled(false);
}
if(a.getActionCommand().equals("200%"))
{
this.zoom(2.0);
enableZoomItems();
twoHundred.setEnabled(false);
}
if(a.getActionCommand().equals("500%"))
{
this.zoom(5.0);
enableZoomItems();
fiveHundred.setEnabled(false);
}
}
/**
* Class for establishing the focus for the textfields
*/
private class PictureExplorerFocusTraversalPolicy
extends FocusTraversalPolicy {
/**
* Method to get the next component for focus
*/
public Component getComponentAfter(Container focusCycleRoot,
Component aComponent) {
if (aComponent.equals(colValue))
return rowValue;
else
return colValue;
}
/**
* Method to get the previous component for focus
*/
public Component getComponentBefore(Container focusCycleRoot,
Component aComponent) {
if (aComponent.equals(colValue))
return rowValue;
else
return colValue;
}
public Component getDefaultComponent(Container focusCycleRoot) {
return colValue;
}
public Component getLastComponent(Container focusCycleRoot) {
return rowValue;
}
public Component getFirstComponent(Container focusCycleRoot) {
return colValue;
}
}
/**
* Test Main. It will explore the beach
*/
public static void main( String args[])
{
Picture pix = new Picture("flower2.jpg");
//Picture smallP = pix.scale(0.25, 0.25);
//smallP.write("smallkai.jpg");
pix.explore();
//Picture p = new SimplePicture();
}
}